const algorithms = {
    Atkinson: [
        [0, 0, 1 / 8, 1 / 8],
        [1 / 8, 1 / 8, 1 / 8, 0],
        [0, 1 / 8, 0, 0]
    ],
    Burkes: [
        [0, 0, 0, 8 / 32, 4 / 32],
        [2 / 32, 4 / 32, 8 / 32, 4 / 32, 2 / 32]
    ],
    FloydSteinburg: [
        [0, 0, 7 / 16],
        [3 / 16, 5 / 16, 1 / 16]
    ],
    JarvisJudiceNinke: [
        [0, 0, 0, 7 / 48, 5 / 48],
        [3 / 48, 5 / 48, 7 / 48, 5 / 48, 3 / 48],
        [1 / 48, 3 / 48, 5 / 48, 3 / 48, 1 / 48]
    ],
    Sierra2: [
        [0, 0, 0, 4 / 16, 3 / 16],
        [1 / 16, 2 / 16, 3 / 16, 2 / 16, 1 / 16]
    ],
    Sierra3: [
        [0, 0, 0, 5 / 32, 3 / 32],
        [2 / 32, 4 / 32, 5 / 32, 4 / 32, 2 / 32],
        [0, 2 / 32, 3 / 32, 2 / 32, 0]
    ],
    SierraLite: [
        [0, 0, 2 / 4],
        [1 / 4, 1 / 4, 0]
    ],
    Stucki: [
        [0, 0, 0, 8 / 42, 4 / 42],
        [2 / 42, 4 / 42, 8 / 42, 4 / 42, 2 / 42],
        [1 / 42, 2 / 42, 4 / 42, 2 / 42, 1 / 42]
    ]
};

function bit(x, threshold) {
    if (x >= threshold) {
        return 255;
    } else {
        return 0;
    }
}

function normalize(x) {
    if (x < 0) {
        return 0;
    } else if (x > 255) {
        return 255;
    }
    return Math.round(x);
}

dither = function(data, w, h, algorithm, threshold){
    const matrix = algorithms[algorithm];
    const matrixHeight = matrix.length;
    const matrixWidth = matrix[0].length;

    let matrixOffset = 0;
    for (let k = 1; k < matrixWidth; k++) {
        if (matrix[0][k] > 0) {
            matrixOffset = k - 1;
            break;
        }
    }
    for (let y = 0; y < h; y++) {
        const reverse = (y & 1) === 1;

        for (let x = reverse ? w - 1 : 0; reverse ? x >= 0 : x < w; reverse ? x-- : x++) {
            // const index = (y * w + x) << 2;
            const origin = data[x][y];

            data[x][y] = bit(origin, threshold);
            const err = origin - data[x][y];

            for (let i = 0; i < matrixWidth; i++) {
                for (let j = 0; j < matrixHeight; j++) {
                    if (matrix[j][i] > 0) {
                        const x2 = reverse ? x - (i - matrixOffset) : x + (i - matrixOffset);
                        const y2 = y + j;
                        if (x2 >= 0 && x2 < w && y2 < h) {
                            // const idx2 = index + (x2 - x) * 4 + (y2 - y) * w * 4;
                            data[x2][y2] = normalize(data[x2][y2] + matrix[j][i] * err);
                        }
                    }
                }
            }
        }
    }
}

module.exports = dither;